`timescale 1ns / 1ps

module tb_pwc2_layer_L6();

parameter N_INH = 4;
parameter N_INW = 10;
parameter N_OUT = 4;
parameter N_ACT = 1;
parameter N_ICH = 1280;
parameter N_OCH = 96;
parameter BIT_IN = 8;
parameter BIT_WT = 5;
parameter BIT_OUT = 8;
parameter VEC_LEN = 1 * 16;
parameter ITER = VEC_LEN * N_ICH / N_INW;
parameter OUT_LEN = VEC_LEN * N_OCH / N_ACT;
parameter fname = "L10_ACTV_u8.bin";
parameter fgold = "L11_ACTV_u8.bin";

localparam IN_W  = N_INW * BIT_IN;
localparam OUT_W = N_ACT * BIT_OUT;
localparam FILE_BLKSIZE =  IN_W / 8;
localparam GOLD_BLKSIZE = OUT_W / 8;

reg     rst;
reg     clk;
reg     [ IN_W-1: 0]    i_data;
wire    [OUT_W-1: 0]    o_data;
reg     i_vld, o_rdy;
wire    i_rdy, o_vld;

pwc2_layer #(
    .N_INH        ( N_INH         ),
    .N_INW        ( N_INW         ),
    .N_OUT        ( N_OUT         ),
    .N_ACT        ( N_ACT         ),
    .N_ICH        ( N_ICH         ),
    .N_OCH        ( N_OCH         ),
    .BIT_IN       ( BIT_IN        ),
    .BIT_WT       ( BIT_WT        ),
    .BIT_CV       ( 16            ),
    .BIT_NB       ( 16            ),
    .BIT_NM       ( 16            ),
    .BIT_OT       ( BIT_OUT       ),
    .PWCV_PE      ( "DSP"         ),
    .PWET_FILE    ( "L11_PW.mem"  ),
    .PWET_TYPE    ( "auto"        ),
    .PWET_LATENCY ( 2             ),
    .PBUF_TYPE    ( "auto"        ),
    .PBUF_LATENCY ( 2             ),
    .NORM_PE      ( "LUT"         ),
    .NORM_FILE    ( "L11_BM.mem"  ),
    .NORM_TYPE    ( "auto"        ),
    .NORM_LATENCY ( 2             ),
    .FIFO_TYPE    ( "auto"        ),
    .FIFO_LATENCY ( 2             )
)
inst_pwc2_layer (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( i_rdy    ),
    .i_vld                   ( i_vld    ),
    .i_data                  ( i_data   ),

    .o_rdy                   ( o_rdy    ),
    .o_vld                   ( o_vld    ),
    .o_data                  ( o_data   )
);

always #5 clk = ~clk;

integer fd_i;
integer fd_o;
reg [IN_W - 1 : 0]  file_buf;
reg [7 : 0]         file    [FILE_BLKSIZE-1 : 0];
reg [7 : 0]         gold    [GOLD_BLKSIZE-1 : 0];

initial
begin
    fd_i = $fopen(fname, "rb");
    if (fd_i == 0)
    begin
        $display("Failed to open file %s", fname);
        $stop;
    end

    fd_o = $fopen(fgold, "rb");
    if (fd_o == 0)
    begin
        $display("Failed to open file %s", fgold);
        $stop;
    end
end

integer cnt_out = 0;
integer cnt_wrong = 0;
integer it;

initial
begin: gen_input
    integer i, j, k;
    integer dice;
    integer rcode;
    clk = 0;
    rst = 1;
    #20;
    rst = 0;
    #15;
    for (it = 0; it < ITER; it = it + 1)
    begin
        i_data <= {IN_W{1'bx}};
        i_vld  <= 0;
//        dice = 0;
        // Wait random clock cycles
        dice = {$random()} % 71;
        for (j = 0; j < dice; j = j + 1)
            #10;
        // Load input from file
        rcode = $fread(file, fd_i, 0, FILE_BLKSIZE);
        if (rcode != FILE_BLKSIZE)
            $display("Error in fread(fd_i) %d", it);
        for (k = 0; k < FILE_BLKSIZE; k = k + 1)
            file_buf[8 * k +: 8] = file[k];
        i_data <= file_buf;
        i_vld  <= 1;
        # 10;
        // Wait for handshake
        while (i_rdy == 0)
            # 10;
    end
    i_data <= {IN_W{1'bx}};
    i_vld  <= 0;
    while (cnt_out != OUT_LEN)
        #10;
    # 1000;
    $fclose(fd_i);
    $fclose(fd_o);
    if (cnt_wrong == 0)
        $display("Test Pass!");
    else
        $display("Test Failed, %d", cnt_wrong);
    $stop;
end

always @(posedge clk)
begin
    if (rst)
    begin
        i_vld <= 1'b0;
        o_rdy <= 1'b0;
    end
    else
    begin
        o_rdy <= |({$random()} % 4);
//        o_rdy <= 1;
    end
end

reg [OUT_W-1: 0] gold_buf;
reg correct;

always @(posedge clk)
begin: check_output
    integer k;
    integer rcode;
    if (o_vld & o_rdy)
    begin
        // load golden result
        rcode = $fread(gold, fd_o, 0, GOLD_BLKSIZE);
        if (rcode != GOLD_BLKSIZE)
            $display("Error in fread(fd_o) %d", cnt_out);
        for (k = 0; k < GOLD_BLKSIZE; k = k + 1)
        begin
            gold_buf[8 * k +: 8] = gold[k];
        end
        // check output
        correct = (gold_buf == o_data);
        if (~correct)
        begin
            $display("Wrong @ %h", cnt_out);
            cnt_wrong = cnt_wrong + 1;
        end
        cnt_out = cnt_out + 1;
    end
end

endmodule
